#include <windows.h>               // standardowy plik naglowkowy Windows
#include <iostream>
#include "glew.h"
#include <fcntl.h>
#include <io.h>
#include <sstream>
#include <assert.h>

using namespace std;

//------------------------------------ RedirectIOToConsole -------------------------------------------------------------

namespace EngineTools
{

static const int MAX_CONSOLE_LINES = 500;
void RedirectIOToConsole()
{

	int hConHandle;

	long lStdHandle;

	CONSOLE_SCREEN_BUFFER_INFO coninfo;

	FILE *fp;

	// allocate a console for this app

	AllocConsole();

	// set the screen buffer to be big enough to let us scroll text

	GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),

	&coninfo);

	coninfo.dwSize.Y = MAX_CONSOLE_LINES;

	SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),

	coninfo.dwSize);

	// redirect unbuffered STDOUT to the console

	lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);

	hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);

	fp = _fdopen( hConHandle, "w" );

	*stdout = *fp;

	setvbuf( stdout, NULL, _IONBF, 0 );

	// redirect unbuffered STDIN to the console

	lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);

	hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);

	fp = _fdopen( hConHandle, "r" );

	*stdin = *fp;

	setvbuf( stdin, NULL, _IONBF, 0 );

	// redirect unbuffered STDERR to the console

	lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);

	hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);

	fp = _fdopen( hConHandle, "w" );

	*stderr = *fp;

	setvbuf( stderr, NULL, _IONBF, 0 );

	// make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog

	// point to console as well

	ios::sync_with_stdio();

}

//------------------------------------ isBitSet -----------------------------------------------------------------------
bool isBitSet( const unsigned int mode, short pos )
{
     if( mode & ( 1  << (32-pos) ) )
         return true;
     
     return false;
}

//------------------------------------------ TextFileRead -------------------------------------------------------------
char* TextFileRead(char *fn) {


	FILE *fp;
	char *content = NULL;

	int count=0;

	if (fn != NULL) {
		fp = fopen(fn,"rt");

		if (fp != NULL) {
      
      fseek(fp, 0, SEEK_END);
      count = ftell(fp);
      rewind(fp);

			if (count > 0) {
				content = (char *)malloc(sizeof(char) * (count+1));
				count = fread(content,sizeof(char),count,fp);
				content[count] = '\0';
			}
			fclose(fp);
		}
	}
	return content;
}

//---------------------------------------------- TextFileWrite -----------------------------------------------------
int TextFileWrite(char *fn, char *s) {

	FILE *fp;
	int status = 0;

	if (fn != NULL) {
		fp = fopen(fn,"w");

		if (fp != NULL) {
			
			if (fwrite(s,sizeof(char),strlen(s),fp) == strlen(s))
				status = 1;
			fclose(fp);
		}
	}
	return(status);
}

//------------------------------------ GetOpenglError -----------------------------------------------------//
std::string GetOpenglError()
{
	int er = glGetError();
	std::stringstream str;

    if (er == GL_INVALID_ENUM)
    {
            str << "Error: INVALID_ENUM" << endl;
    }
    else
    if (er == GL_INVALID_VALUE)
    {
            str << "Error: GL_INVALID_VALUE" << endl;
    }
    else
    if (er == GL_INVALID_OPERATION)
    {
            str << "Error: GL_INVALID_OPERATION" << endl;
    }
    else
    if (er == GL_STACK_OVERFLOW)
    {
            str << "Error: GL_STACK_OVERFLOW" << endl;
    }
    else
    if (er == GL_STACK_UNDERFLOW)
    {
            str << "Error: GL_STACK_UNDERFLOW" << endl;
    }
    else
    if (er == GL_OUT_OF_MEMORY)
    {
            str << "Error: GL_OUT_OF_MEMORY" << endl;
    }
    else
    if (er == GL_TABLE_TOO_LARGE)
    {
            str << "Error: GL_TABLE_TOO_LARGE" << endl;
    }

	return str.str();
}

//-------------------------------- DebugUniform ------------------------------//
std::string DebugUniform( int progHandle, int uniformIndex, int howManyValues )
{
	float t[] = {-11, -11, -11, -11};
	glGetUniformfv( progHandle, uniformIndex, t );

	std::stringstream str;
	for( int ii = 0; ii < howManyValues; ++ii )
	{
		str << fixed <<  t[ii] << ", ";
	}
	return str.str();
}

bool GetUniformPositionFromShader(unsigned int& positionInShader, unsigned int shader, std::string variableNameInShader, bool assertOnFailure)
{
	positionInShader = glGetUniformLocation(shader, variableNameInShader.c_str());

	if (positionInShader == -1)
	{
		if (assertOnFailure)
		{
			stringstream msg;
			msg << "GetUniformPositionFromShader: failed to get position in shader= " << shader << " for name= " << variableNameInShader << endl;
			assert(false && msg.str().c_str());
		}
		return false;
	}
	
	return true;
}

//------------------------------------ LoadCubemap ---------------------------------------------------------------------
//void LoadCubemap()
//{
//	cm = SOIL_load_OGL_cubemap
//		(
//			"modele\\tekstury\\cm\\right.jpg",
//			"modele\\tekstury\\cm\\left.jpg",
//			"modele\\tekstury\\cm\\top.jpg",
//			"modele\\tekstury\\cm\\bottom.jpg",
//			"modele\\tekstury\\cm\\front.jpg",
//			"modele\\tekstury\\cm\\back.jpg",
//			SOIL_LOAD_RGB,
//			SOIL_CREATE_NEW_ID,
//			SOIL_FLAG_MIPMAPS
//		);
//
//	if( cm == 0 )
//	{
//		cout << "cubemap loading failed: " << SOIL_last_result() << endl;
//	}
//}

//------------------------------------------- drawRectangle ------------------------------------------------
//void drawRectangle( GLfloat leftDownX, GLfloat leftDownY )
//{
//	glBegin( GL_QUADS );
//		glVertex2f( leftDownX, leftDownY );
//		glVertex2f( leftDownX + 1.0f, leftDownY + 0.0f );
//		glVertex2f( leftDownX + 1.0f, leftDownY + 1.0f );
//		glVertex2f( leftDownX + 0.0f, leftDownY + 1.0f );
//	glEnd();
//}
}